package com.tencent.tmm.knoi.dataclass

import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.symbol.*
import com.google.devtools.ksp.validate
import com.squareup.kotlinpoet.*
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.ksp.toClassName
import com.squareup.kotlinpoet.ksp.toTypeName
import com.squareup.kotlinpoet.ksp.writeTo
import com.tencent.tmm.knoi.annotation.KnoiExport
import com.tencent.tmm.knoi.annotation.KnoiIgnore
import com.tencent.tmm.knoi.utils.isOhosArm64

/**
 * 处理 @KnoiExport 注解的数据类，自动生成 toJSValue() 扩展函数
 */
fun processDataClassExport(
    codeGenerator: CodeGenerator,
    resolver: Resolver,
    options: Map<String, String>
): List<DataClassExportInfo> {
    if (!isOhosArm64(options)) {
        return emptyList()
    }

    val exportDataClasses = parseExportDataClasses(resolver)
    if (exportDataClasses.isEmpty()) {
        return emptyList()
    }

    // 生成通用的 Map 和 List 扩展函数
    generateCommonExtensions(codeGenerator, exportDataClasses)

    // 生成数据类的扩展函数
    generateDataClassExtensions(codeGenerator, exportDataClasses)

    return exportDataClasses
}

/**
 * 解析所有标记了 @KnoiExport 的数据类
 */
private fun parseExportDataClasses(resolver: Resolver): List<DataClassExportInfo> {
    val knoiExport = KnoiExport::class.qualifiedName!!
    val symbols = resolver.getSymbolsWithAnnotation(knoiExport)

    return symbols
        .filter { it is KSClassDeclaration && it.validate() }
        .map { it as KSClassDeclaration }
        .filter { it.modifiers.contains(Modifier.DATA) }
        .map { classDeclaration ->
            DataClassExportInfo(
                className = classDeclaration.toClassName(),
                packageName = classDeclaration.packageName.asString(),
                properties = parseProperties(classDeclaration),
                customName = getAnnotationValue(classDeclaration, knoiExport, "name", "")
            )
        }
        .toList()
}

/**
 * 解析数据类的属性
 */
private fun parseProperties(classDeclaration: KSClassDeclaration): List<PropertyInfo> {
    val knoiIgnore = KnoiIgnore::class.qualifiedName!!

    return classDeclaration.getAllProperties()
        .filter { property ->
            // 过滤掉标记了 @KnoiIgnore 的属性
            property.annotations.none {
                it.annotationType.resolve().declaration.qualifiedName?.asString() == knoiIgnore
            }
        }
        .map { property ->
            PropertyInfo(
                name = property.simpleName.asString(),
                type = property.type.resolve(),
                isNullable = property.type.resolve().isMarkedNullable
            )
        }
        .toList()
}

/**
 * 生成通用的 Map 和 List 扩展函数
 */
private fun generateCommonExtensions(
    codeGenerator: CodeGenerator,
    exportDataClasses: List<DataClassExportInfo>
) {
    val jsValueClassName = ClassName("com.tencent.tmm.knoi.type", "JSValue")

    val fileSpec = FileSpec.builder("com.tencent.tmm.knoi.extensions", "CommonJSValueExtensions")
        .addImport("com.tencent.tmm.knoi.type", "JSValue")
        .addImport("kotlinx.cinterop", "ExperimentalForeignApi")
        .addAnnotation(
            AnnotationSpec.builder(ClassName("kotlin", "OptIn"))
                .addMember("%T::class", ClassName("kotlinx.cinterop", "ExperimentalForeignApi"))
                .build()
        )
        // 1. 先生成基础的 Map 和 List 扩展函数
        .addFunction(generateMapToJSValueFunction(jsValueClassName, exportDataClasses))
        .addFunction(generateListToJSValueFunction(jsValueClassName, exportDataClasses))
        // 2. 生成具体类型的扩展函数
        .apply {
            generateTypedListExtensions(this, exportDataClasses, jsValueClassName)
            generateTypedMapExtensions(this, exportDataClasses, jsValueClassName)
        }
        .build()

    fileSpec.writeTo(codeGenerator, aggregating = true)
}

/**
 * 生成 Map<String, Any?>.toJSValue() 扩展函数
 */
private fun generateMapToJSValueFunction(
    jsValueClassName: ClassName,
    exportDataClasses: List<DataClassExportInfo>
): FunSpec {
    val mapType = ClassName("kotlin.collections", "Map")
    val stringType = ClassName("kotlin", "String")
    val anyType = ClassName("kotlin", "Any").copy(nullable = true)
    val receiverType = mapType.parameterizedBy(stringType, anyType)

    return FunSpec.builder("toJSValue")
        .receiver(receiverType)
        .returns(jsValueClassName)
        .addCode(buildString {
            appendLine("val jsObject = JSValue.createJSObject()")
            appendLine("this.forEach { (key, value) ->")
            appendLine("    jsObject[key] = when (value) {")
            appendLine("        null -> JSValue.createJSObject()")
            appendLine("        is String -> JSValue.createJSValue(value)")
            appendLine("        is Int -> JSValue.createJSValue(value)")
            appendLine("        is Long -> JSValue.createJSValue(value)")
            appendLine("        is Double -> JSValue.createJSValue(value)")
            appendLine("        is Float -> JSValue.createJSValue(value)")
            appendLine("        is Boolean -> JSValue.createJSValue(value)")
            appendLine("        is Byte -> JSValue.createJSValue(value.toInt())")
            appendLine("        is Short -> JSValue.createJSValue(value.toInt())")
            appendLine("        is Char -> JSValue.createJSValue(value.toString())")

            // 添加所有导出的数据类
            exportDataClasses.forEach { dataClassInfo ->
                val fullClassName = "${dataClassInfo.packageName}.${dataClassInfo.className.simpleName}"
                appendLine("        is $fullClassName -> value.toJSValue()")
            }

            appendLine("        is List<*> -> {")
            appendLine("            @Suppress(\"UNCHECKED_CAST\")")
            appendLine("            (value as List<Any?>).toJSValue()")
            appendLine("        }")
            appendLine("        is Map<*, *> -> {")
            appendLine("            @Suppress(\"UNCHECKED_CAST\")")
            appendLine("            (value as Map<String, Any?>).toJSValue()")
            appendLine("        }")
            appendLine("        else -> JSValue.createJSValue(value.toString())")
            appendLine("    }")
            appendLine("}")
            append("return jsObject")
        })
        .build()
}

/**
 * 生成 List<Any?>.toJSValue() 扩展函数
 */
private fun generateListToJSValueFunction(
    jsValueClassName: ClassName,
    exportDataClasses: List<DataClassExportInfo>
): FunSpec {
    val listType = ClassName("kotlin.collections", "List")
    val anyType = ClassName("kotlin", "Any").copy(nullable = true)
    val receiverType = listType.parameterizedBy(anyType)

    return FunSpec.builder("toJSValue")
        .receiver(receiverType)
        .returns(jsValueClassName)
        .addCode(buildString {
            appendLine("val tempList = List(this.size) { 0 }")
            appendLine("val jsArray = JSValue.createJSValue(tempList)")
            appendLine("this.forEachIndexed { index, value ->")
            appendLine("    jsArray[index.toString()] = when (value) {")
            appendLine("        null -> JSValue.createJSObject()")
            appendLine("        is String -> JSValue.createJSValue(value)")
            appendLine("        is Int -> JSValue.createJSValue(value)")
            appendLine("        is Long -> JSValue.createJSValue(value)")
            appendLine("        is Double -> JSValue.createJSValue(value)")
            appendLine("        is Float -> JSValue.createJSValue(value)")
            appendLine("        is Boolean -> JSValue.createJSValue(value)")
            appendLine("        is Byte -> JSValue.createJSValue(value.toInt())")
            appendLine("        is Short -> JSValue.createJSValue(value.toInt())")
            appendLine("        is Char -> JSValue.createJSValue(value.toString())")

            // 先判断是否为导出的数据类（必须在 List 和 Map 之前）
            exportDataClasses.forEach { dataClassInfo ->
                val fullClassName = "${dataClassInfo.packageName}.${dataClassInfo.className.simpleName}"
                appendLine("        is $fullClassName -> value.toJSValue()")
            }

            // 再判断是否为 List 或 Map
            appendLine("        is List<*> -> {")
            appendLine("            @Suppress(\"UNCHECKED_CAST\")")
            appendLine("            (value as List<Any?>).toJSValue()")
            appendLine("        }")
            appendLine("        is Map<*, *> -> {")
            appendLine("            @Suppress(\"UNCHECKED_CAST\")")
            appendLine("            (value as Map<String, Any?>).toJSValue()")
            appendLine("        }")
            appendLine("        else -> JSValue.createJSValue(value.toString())")
            appendLine("    }")
            appendLine("}")
            append("return jsArray")
        })
        .build()
}

/**
 * 为具体类型的 List 生成扩展函数
 */
private fun generateTypedListExtensions(
    fileBuilder: FileSpec.Builder,
    exportDataClasses: List<DataClassExportInfo>,
    jsValueClassName: ClassName
) {
    val listType = ClassName("kotlin.collections", "List")

    // 为每个导出的数据类生成 List<DataClass>.toJSValue()
    exportDataClasses.forEach { dataClassInfo ->
        val className = dataClassInfo.className
        val receiverType = listType.parameterizedBy(className)

        fileBuilder.addFunction(
            FunSpec.builder("toJSValue")
                .receiver(receiverType)
                .returns(jsValueClassName)
                .addCode("""
                    |val tempList = List(this.size) { 0 }
                    |val jsArray = JSValue.createJSValue(tempList)
                    |this.forEachIndexed { index, item ->
                    |    jsArray[index.toString()] = item.toJSValue()
                    |}
                    |return jsArray
                """.trimMargin())
                .build()
        )
    }

    // 为所有基本类型生成扩展函数
    val basicTypes = listOf(
        ClassName("kotlin", "String"),
        ClassName("kotlin", "Int"),
        ClassName("kotlin", "Long"),
        ClassName("kotlin", "Double"),
        ClassName("kotlin", "Float"),
        ClassName("kotlin", "Boolean"),
        ClassName("kotlin", "Byte"),
        ClassName("kotlin", "Short"),
        ClassName("kotlin", "Char")
    )

    basicTypes.forEach { type ->
        val receiverType = listType.parameterizedBy(type)

        // 对于需要转换的类型，使用特殊处理
        val codeBlock = when (type.simpleName) {
            "Byte", "Short" -> {
                // 转换为 Int 列表
                """
                    |val intList = this.map { it.toInt() }
                    |return JSValue.createJSValue(intList)
                """.trimMargin()
            }
            "Char" -> {
                // 转换为 String 列表
                """
                    |val stringList = this.map { it.toString() }
                    |return JSValue.createJSValue(stringList)
                """.trimMargin()
            }
            else -> {
                "return JSValue.createJSValue(this)"
            }
        }

        fileBuilder.addFunction(
            FunSpec.builder("toJSValue")
                .receiver(receiverType)
                .returns(jsValueClassName)
                .addCode(codeBlock)
                .build()
        )
    }
}

/**
 * 为具体类型的 Map 生成扩展函数
 */
private fun generateTypedMapExtensions(
    fileBuilder: FileSpec.Builder,
    exportDataClasses: List<DataClassExportInfo>,
    jsValueClassName: ClassName
) {
    val mapType = ClassName("kotlin.collections", "Map")
    val listType = ClassName("kotlin.collections", "List")
    val stringType = ClassName("kotlin", "String")

    // 为 Map<String, DataClass> 生成扩展函数
    exportDataClasses.forEach { dataClassInfo ->
        val className = dataClassInfo.className

        // Map<String, DataClass>
        val receiverType1 = mapType.parameterizedBy(stringType, className)

        fileBuilder.addFunction(
            FunSpec.builder("toJSValue")
                .receiver(receiverType1)
                .returns(jsValueClassName)
                .addCode("""
                    |val jsObject = JSValue.createJSObject()
                    |this.forEach { (key, value) ->
                    |    jsObject[key] = value.toJSValue()
                    |}
                    |return jsObject
                """.trimMargin())
                .build()
        )

        // Map<String, List<DataClass>>
        val listOfDataClass = listType.parameterizedBy(className)
        val receiverType2 = mapType.parameterizedBy(stringType, listOfDataClass)

        fileBuilder.addFunction(
            FunSpec.builder("toJSValue")
                .receiver(receiverType2)
                .returns(jsValueClassName)
                .addCode("""
                    |val jsObject = JSValue.createJSObject()
                    |this.forEach { (key, value) ->
                    |    jsObject[key] = value.toJSValue()
                    |}
                    |return jsObject
                """.trimMargin())
                .build()
        )
    }

    // 为 Map<String, 基本类型> 生成扩展函数
    val basicTypes = listOf(
        ClassName("kotlin", "String"),
        ClassName("kotlin", "Int"),
        ClassName("kotlin", "Long"),
        ClassName("kotlin", "Double"),
        ClassName("kotlin", "Float"),
        ClassName("kotlin", "Boolean")
    )

    basicTypes.forEach { type ->
        val receiverType = mapType.parameterizedBy(stringType, type)

        fileBuilder.addFunction(
            FunSpec.builder("toJSValue")
                .receiver(receiverType)
                .returns(jsValueClassName)
                .addCode("""
                    |val jsObject = JSValue.createJSObject()
                    |this.forEach { (key, value) ->
                    |    jsObject[key] = JSValue.createJSValue(value)
                    |}
                    |return jsObject
                """.trimMargin())
                .build()
        )
    }
}

/**
 * 生成数据类的扩展函数
 */
private fun generateDataClassExtensions(
    codeGenerator: CodeGenerator,
    exportDataClasses: List<DataClassExportInfo>
) {
    val jsValueClassName = ClassName("com.tencent.tmm.knoi.type", "JSValue")

    exportDataClasses.forEach { dataClassInfo ->
        val fileSpec = FileSpec.builder(
            dataClassInfo.packageName,
            "${dataClassInfo.className.simpleName}JSValueExt"
        )
            .addImport("com.tencent.tmm.knoi.type", "JSValue")
            .addImport("com.tencent.tmm.knoi.extensions", "toJSValue")
            .addImport("kotlinx.cinterop", "ExperimentalForeignApi")
            .addAnnotation(
                AnnotationSpec.builder(ClassName("kotlin", "OptIn"))
                    .addMember("%T::class", ClassName("kotlinx.cinterop", "ExperimentalForeignApi"))
                    .build()
            )
            .addFunction(generateToJSValueFunction(dataClassInfo, jsValueClassName))
            .build()

        fileSpec.writeTo(codeGenerator, aggregating = true)
    }
}

/**
 * 生成 toJSValue() 扩展函数
 */
private fun generateToJSValueFunction(
    dataClassInfo: DataClassExportInfo,
    jsValueClassName: ClassName
): FunSpec {
    val className = dataClassInfo.className

    return FunSpec.builder("toJSValue")
        .receiver(className)
        .returns(jsValueClassName)
        .addCode(buildString {
            appendLine("val jsValue = JSValue.createJSObject()")

            dataClassInfo.properties.forEach { property ->
                val propertyName = property.name

                when {
                    isBasicType(property.type) -> {
                        if (property.isNullable) {
                            appendLine("this.$propertyName?.let { jsValue[\"$propertyName\"] = JSValue.createJSValue(it) }")
                        } else {
                            appendLine("jsValue[\"$propertyName\"] = JSValue.createJSValue(this.$propertyName)")
                        }
                    }
                    isListType(property.type) -> {
                        if (property.isNullable) {
                            appendLine("this.$propertyName?.let { jsValue[\"$propertyName\"] = it.toJSValue() }")
                        } else {
                            appendLine("jsValue[\"$propertyName\"] = this.$propertyName.toJSValue()")
                        }
                    }
                    isMapType(property.type) -> {
                        if (property.isNullable) {
                            appendLine("this.$propertyName?.let { jsValue[\"$propertyName\"] = it.toJSValue() }")
                        } else {
                            appendLine("jsValue[\"$propertyName\"] = this.$propertyName.toJSValue()")
                        }
                    }
                    isExportedDataClass(property.type) -> {
                        if (property.isNullable) {
                            appendLine("this.$propertyName?.let { jsValue[\"$propertyName\"] = it.toJSValue() }")
                        } else {
                            appendLine("jsValue[\"$propertyName\"] = this.$propertyName.toJSValue()")
                        }
                    }
                    else -> {
                        // 其他类型转为字符串
                        if (property.isNullable) {
                            appendLine("this.$propertyName?.let { jsValue[\"$propertyName\"] = JSValue.createJSValue(it.toString()) }")
                        } else {
                            appendLine("jsValue[\"$propertyName\"] = JSValue.createJSValue(this.$propertyName.toString())")
                        }
                    }
                }
            }

            append("return jsValue")
        })
        .build()
}

/**
 * 检查是否为基本类型
 */
private fun isBasicType(type: KSType): Boolean {
    val typeName = type.toClassName().canonicalName
    return typeName in listOf(
        "kotlin.String",
        "kotlin.Int",
        "kotlin.Long",
        "kotlin.Double",
        "kotlin.Float",
        "kotlin.Boolean",
        "kotlin.Byte",
        "kotlin.Short",
        "kotlin.Char"
    )
}

/**
 * 检查是否为标记了 @KnoiExport 的数据类
 */
private fun isExportedDataClass(type: KSType): Boolean {
    val declaration = type.declaration
    if (declaration !is KSClassDeclaration) return false

    return declaration.modifiers.contains(Modifier.DATA) &&
            declaration.annotations.any {
                it.annotationType.resolve().declaration.qualifiedName?.asString() ==
                        KnoiExport::class.qualifiedName
            }
}

/**
 * 检查是否为 List 类型
 */
private fun isListType(type: KSType): Boolean {
    val typeName = type.toClassName().canonicalName
    return typeName.startsWith("kotlin.collections.List") ||
            typeName.startsWith("kotlin.collections.ArrayList") ||
            typeName.startsWith("kotlin.collections.MutableList")
}

/**
 * 检查是否为 Map 类型
 */
private fun isMapType(type: KSType): Boolean {
    val typeName = type.toClassName().canonicalName
    return typeName.startsWith("kotlin.collections.Map") ||
            typeName.startsWith("kotlin.collections.HashMap") ||
            typeName.startsWith("kotlin.collections.MutableMap")
}

/**
 * 获取注解的值
 */
private fun getAnnotationValue(
    declaration: KSAnnotated,
    annotationName: String,
    key: String,
    defaultValue: String
): String {
    val annotation = declaration.annotations.find {
        it.annotationType.resolve().declaration.qualifiedName?.asString() == annotationName
    } ?: return defaultValue

    val value = annotation.arguments.find { it.name?.asString() == key }?.value
    return value as? String ?: defaultValue
}

/**
 * 数据类导出信息
 */
data class DataClassExportInfo(
    val className: ClassName,
    val packageName: String,
    val properties: List<PropertyInfo>,
    val customName: String = ""
)

/**
 * 属性信息
 */
data class PropertyInfo(
    val name: String,
    val type: KSType,
    val isNullable: Boolean
)